import { HttpException, HttpStatus, Injectable } from '@nestjs/common'
import { CreateOperationDto } from './dto/create-operation.dto'
import { UpdateOperationDto } from './dto/update-operation.dto'
import { Operation } from './entities/operation.entity'
import { InjectRepository } from '@nestjs/typeorm'
import { QueryFailedError, Repository } from 'typeorm'
import { HttpExceptionEnum } from 'types/http-exception'
import { generateSelect, hasPermission } from 'src/utils/index'
import { UserFromRequest } from 'types/global'

const operationSelect = generateSelect(Operation)
@Injectable()
export class OperationService {
  constructor(
    @InjectRepository(Operation)
    private readonly operationRepository: Repository<Operation>
  ) {}

  async create(createOperationDto: CreateOperationDto, userId: number) {
    const operation = new Operation(
      Object.assign({}, createOperationDto, {
        createdBy: userId,
        updatedBy: userId
      })
    )
    try {
      return await this.operationRepository.save(operation)
    } catch (error) {
      if (error instanceof QueryFailedError) {
        throw new HttpException(
          HttpExceptionEnum.OPERATION_EXISTED,
          HttpStatus.BAD_REQUEST
        )
      }
      throw error
    }
  }

  async findAll(
    params: {
      keyword?: string
      pageNum?: number
      pageSize?: number
    } = {}
  ): Promise<{
    list: Operation[]
    total: number
    pageNum: number
    pageSize: number
  }> {
    generateSelect(Operation)
    const { keyword = '', pageNum = 1, pageSize = 9999 } = params
    const skip = (pageNum - 1) * pageSize
    const queryBuilder =
      this.operationRepository.createQueryBuilder('operation')

    if (keyword) {
      queryBuilder.where(
        'operation.name LIKE :keyword OR operation.code LIKE :keyword',
        { keyword: `%${keyword}%` }
      )
    }

    // 添加 orderBy 根据 createTime 倒序排列
    queryBuilder.orderBy('operation.createTime', 'DESC')

    queryBuilder
      .leftJoinAndSelect('operation.createdBy', 'createdBy')
      .leftJoinAndSelect('operation.updatedBy', 'updatedBy')

    const [list, total] = await Promise.all([
      queryBuilder
        .skip(skip)
        .take(pageSize)
        .select(operationSelect)
        .getRawMany(),
      queryBuilder.getCount()
    ])

    return {
      list,
      total,
      pageNum,
      pageSize
    }
  }

  async findOne(id: number) {
    const queryBuilder =
      this.operationRepository.createQueryBuilder('operation')
    queryBuilder
      .leftJoinAndSelect('operation.createdBy', 'createdBy')
      .leftJoinAndSelect('operation.updatedBy', 'updatedBy')
    const operation = await queryBuilder
      .where('operation.id = :id', { id })
      .select(operationSelect)
      .getRawOne()

    if (!operation)
      throw new HttpException(
        HttpExceptionEnum.OPERATION_NOT_EXIST,
        HttpStatus.BAD_REQUEST
      )
    return operation
  }

  async update(updateOperationDto: UpdateOperationDto, user: UserFromRequest) {
    try {
      const operation = await this.findOne(updateOperationDto.id)
      if (!operation)
        throw new HttpException(
          HttpExceptionEnum.OPERATION_EXISTED,
          HttpStatus.BAD_REQUEST
        )
      hasPermission(user, operation.createdById)
      Object.assign(operation, updateOperationDto, { updatedBy: user.userId })
      const newoperation = await this.operationRepository.save(operation)

      return newoperation
    } catch (error) {
      if (error instanceof QueryFailedError) {
        throw new HttpException(
          HttpExceptionEnum.OPERATION_EXISTED,
          HttpStatus.BAD_REQUEST
        )
      }
      throw error
    }
  }

  async remove(id: number, user: UserFromRequest) {
    try {
      const operation = await this.findOne(id)
      if (!operation) {
        throw new HttpException(
          HttpExceptionEnum.OPERATION_NOT_EXIST,
          HttpStatus.BAD_REQUEST
        )
      }
      hasPermission(user, operation.createdById)
      await this.operationRepository.remove(operation)

      return `operation with id ${id} has been removed`
    } catch (error) {
      throw error
    }
  }
}
